Skip to content

feat: better grpc handling - #1104

Merged
michaelboulton merged 15 commits into
masterfrom
feat/grpc-better-err-handling
Oct 16, 2026
Merged

michaelboulton merged 15 commits into
masterfrom
feat/grpc-better-err-handling

Conversation

@michaelboulton

@michaelboulton michaelboulton commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

change grpc to use 'error_message' for the actual server error message, and 'details' for the rpc details returned in the trailer.

Technically a breaking change(?) Keeping backwqrd compatability for now

Old style, where details was the error message string from the server:

grpc_response:
  status: "INVALID_ARGUMENT"
  details: "no name given"

New style, where error_message is the error message string and details is the list of messages in the google.rpc.Status from the grpc-status-details-bin trailer:

grpc_response:
  status: "INVALID_ARGUMENT"
  error_message: "no name given"
  details:
    - "@type": type.googleapis.com/google.rpc.BadRequest
      field_violations:
        - field: name
          description: "name must not be empty"

Summary by CodeRabbit

  • New Features
    • gRPC tests can now validate server error messages, including exact text and pattern matches, and structured error details.
    • Added example responses with rich error details for requests with empty names.
  • Bug Fixes
    • Clarified that response bodies cannot be checked when a gRPC call returns a non-OK status; specifying an expected body in this case is reported as a warning.
  • Documentation
    • Expanded the gRPC guide with examples for matching error messages and validating structured status details.

GRPCResponse._handle_grpc_response returned early for any non-OK
response, which made both of the checks below it unreachable: expecting
a 'body' alongside a non-OK status silently passed.

Move the 'body specified but expected status is not OK' check above the
early return, and fetch the response message inside a try/except so a
failed call reports that the expected body could not be checked instead
of being skipped.

The grpc unit tests now assert the exception each spec is expected to
raise and the status the server actually returned, rather than calling
pytest.xfail() before doing any work.
The 'details' key of a grpc response was unusable: the code compared it
against the details string of the response, but the schema said it was an
object, so the only value it could ever have matched was rejected before
the test ran.

Split the two things which can be called the 'details' of a response:

- 'error_message' checks the string the server passed to set_details or
  abort. It goes through the normal matching, so type sentinels like
  !re_search can be used on it.
- 'details' checks the error details attached to the response - the
  messages packed into the google.rpc.Status in the trailing metadata,
  each converted to a mapping including its '@type'. As documented in
  https://grpc.io/docs/guides/error/ this is a Google convention for
  using grpc with protobuf rather than part of grpc itself, but as
  protobuf is what Tavern officially supports that is how it is checked.

Fields which the server did not set are left out of the details, so only
the fields actually returned need to be specified even with strict key
checking on.

The grpc example server now aborts with a google.rpc.Status when given an
empty name so there is something real to check against, and the grpc
plugin is registered in the unit test conftest so grpc test files can be
schema checked there.
The single parametrized test covered three different things at once, with
the spec carrying fields which only applied to some of them. Split it into
one test per kind of spec:

- test_grpc_bad_request: requests which don't match the service definition
  and never reach the server
- test_grpc: requests which run and return the status, error message and
  details the test expects
- test_grpc_bad_response: requests which run, but where the expected
  response doesn't match what the server returned

The failure specs all assert on a substring of the error as well as its
type, so they have to fail for the reason they were supposed to. Also
renamed that field from 'expected_error' so it isn't confused with the
'error_message' of the response block, and removed 'actual_code' - the
status is checked against 'code' in the tests which are expected to pass,
and by the response verification itself in the ones which aren't.
…r-handling

# Conflicts:
#	tavern/_plugins/grpc/response.py
Fold _get_rich_details into _verify_details, so that a details parse
failure no longer also reports that there were no details. Add a test
for details that cannot be decoded, use error_message in the pydantic
spec test, and remove a stale TODO.
…entinels

error_message takes a string or a str, regex or anything sentinel, and
details takes a list or a list or anything sentinel. Test that sentinels
are accepted and that other types are rejected.
The status check already fails the test in this case, so this message
does not need to be a second error.
…r-handling

# Conflicts:
#	tavern/_core/pydantic_models.py
#	tests/unit/test_pydantic_models.py
@michaelboulton
michaelboulton marked this pull request as ready for review October 14, 2026 17:21
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 564429cd-1549-42b0-bff7-57f59b30fc43
📥 Commits

Reviewing files that changed from the base of the PR and between fde69d2 and 8377be7.

📒 Files selected for processing: 11
  • docs/source/grpc.md
  • example/grpc/tavern_grpc_example/server.py
  • example/grpc/tests/test_grpc.tavern.yaml
  • tavern/_core/pydantic_models.py
  • tavern/_core/schema/tests.schema.yaml
  • tavern/_plugins/grpc/jsonschema.yaml
  • tavern/_plugins/grpc/response.py
  • tests/conftest.py
  • tests/unit/tavern_grpc/test_grpc.py
  • tests/unit/test_pydantic_models.py
  • tests/unit/test_schema.py

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

The change adds gRPC response checks for server error messages and structured Google RPC status details. It updates response schemas and verification, documents the expected values, and adds server examples and tests for matching and failure cases.

Changes: gRPC error response verification

Layer / File(s) Summary
Response expectation contract
tavern/_core/pydantic_models.py, tavern/_core/schema/tests.schema.yaml, tavern/_plugins/grpc/jsonschema.yaml, docs/source/grpc.md, tests/unit/test_pydantic_models.py, tests/unit/test_schema.py
The response contract adds error_message and accepts structured details as a list or supported sentinel. The plugin schema renames proto_body to body. The documentation describes error matching, packed status details, and the restriction on bodies for non-OK statuses. Tests cover valid and invalid expectation values.
Response verification
tavern/_plugins/grpc/response.py, tests/conftest.py, tests/unit/tavern_grpc/test_grpc.py
GRPCResponse matches error text and rich status details, and handles body retrieval for non-OK responses. Unit tests cover matching, verification failures, unparseable details, trailer mismatches, and request validation.
gRPC examples and integration tests
example/grpc/tavern_grpc_example/server.py, example/grpc/tests/test_grpc.tavern.yaml
The example server returns an INVALID_ARGUMENT status with a BadRequest detail when a name is empty. Tavern tests cover error-message matching, structured details, mismatches, absent details, and an expected body with a non-OK status.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~30 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant GRPCResponse
  participant grpc_response
  participant GoogleRPCStatus
  GRPCResponse->>grpc_response: Read error text and trailing metadata
  GRPCResponse->>GoogleRPCStatus: Parse rich status from trailing metadata
  GoogleRPCStatus-->>GRPCResponse: Return status and packed details
  GRPCResponse->>GRPCResponse: Match error_message and details
Loading
Merge Risk: ⚪ Minimal · up to 8377b

No identified issue prevents merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 7 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title is related to the pull request and identifies gRPC handling as the scope, but it does not state the key changes to error messages, rich error details, or non-OK responses.
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 7 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@michaelboulton
michaelboulton merged commit 51cac82 into master Oct 16, 2026
16 checks passed
@michaelboulton
michaelboulton deleted the feat/grpc-better-err-handling branch October 16, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant